package com.maple.p2pclient.p2p;

import android.media.MediaCodecInfo;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.TextView;

import com.maple.p2pclient.R;
import com.maple.p2pclient.app.Const;
import com.maple.p2pclient.core.PeerConnectionObserverAdapter;
import com.maple.p2pclient.core.SdpObserverAdapter;
import com.maple.p2pclient.socket.EventCode;
import com.maple.p2pclient.socket.MySocketIOClient;
import com.maple.p2pclient.socket.SocketCallbackAdapter;
import com.maple.p2pclient.socket.SocketType;
import com.maple.p2pclient.ui.MessageAdapter;
import com.maple.p2pclient.utils.LogUtils;
import com.maple.p2pclient.utils.WebRTCUtils;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.CameraVideoCapturer;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpTransceiver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoEncoderSupportedCallback;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

import java.util.ArrayList;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import static org.webrtc.DefaultVideoEncoderFactoryExtKt.createCustomVideoEncoderFactory;

public class P2PCameraActivity extends AppCompatActivity {

    private ListView lvList;
    private TextView tvState;
    private SurfaceViewRenderer localRenderer;
    private PeerConnectionFactory peerConnectionFactory;
    private PeerConnection peerConnection;

    private CameraVideoCapturer cameraVideoCapturer;

    private VideoTrack videoTrack;
    private AudioTrack audioTrack;

    private EglBase.Context eglBaseContext = EglBase.create().getEglBaseContext();
    private MySocketIOClient socketClient;
    private SocketCallbackAdapter socketCallback = new SocketCallbackAdapter() {
        @Override
        public void onConnected() {
            super.onConnected();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvState.setText("已连接");
                    if(socketClient.isConnected()) {
                        try {
                            JSONObject json = new JSONObject();
                            json.put("code", "online");
                            json.put("type",SocketType.CAMERA.getType());
                            json.put("message","已连接");
                            socketClient.sendEvent("event",json);
                        } catch (JSONException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            });
        }

        @Override
        public void onDisconnect() {
            super.onDisconnect();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvState.setText("断开连接");
                }
            });
        }

        @Override
        public void onConnectError(String error) {
            super.onConnectError(error);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvState.setText("连接错误：" + error);
                }
            });
        }

        @Override
        public void onAnswer(JSONObject json) {
            super.onAnswer(json);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        addItemList(json.toString());

                        String form = json.getString("form");
                        String to = json.getString("to");
                        if(form.equals(SocketType.APP.getType()) && to.equals(SocketType.CAMERA.getType())) {
                            parseAnswer(json);
                        }
                    } catch (JSONException e) {
                        LogUtils.logGGQ("onAnswer ERROR:" + e.getMessage());
                    }
                }
            });
        }

        @Override
        public void onIceCandidate(JSONObject json) {
            super.onIceCandidate(json);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        addItemList(json.toString());

                        String form = json.getString("form");
                        String to = json.getString("to");
                        if(form.equals(SocketType.APP.getType()) && to.equals(SocketType.CAMERA.getType())) {
                            parseIceCandidate(json);
                        }
                    } catch (JSONException e) {
                        LogUtils.logGGQ("onIceCandidate ERROR:" + e.getMessage());
                    }
                }
            });
        }

        @Override
        public void onException(String error) {
            super.onException(error);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvState.setText("异常：" + error);
                }
            });
        }

        @Override
        public void onEvent(JSONObject json) {
            super.onEvent(json);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    addItemList(json.toString());
                    try {
                        String code = json.getString("code");
                        if(code.equals(EventCode.CREATE.getCode())) {
                            String user = json.getString("user");
                            createOffer(user);
                        }
                    } catch (JSONException e) {
                        LogUtils.logGGQ("onEvent ERROR:" + e.getMessage());
                    }
                }
            });
        }
    };

    private MessageAdapter listAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_p2p_camera);
        tvState = findViewById(R.id.tv_state);
        lvList = findViewById(R.id.lv_list);
        localRenderer = findViewById(R.id.svr_local_renderer);
        localRenderer.init(eglBaseContext,null);
        localRenderer.setMirror(true);

        listAdapter = new MessageAdapter(this,new ArrayList<String>());
        lvList.setAdapter(listAdapter);

        socketClient = new MySocketIOClient();
        socketClient.setToken(SocketType.CAMERA.getType());
        socketClient.setCallback(socketCallback);
        socketClient.connect(Const.SOCKET_URL);
        initRTC();
    }


    private void initRTC() {
        PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();

        DefaultVideoEncoderFactory encoderFactory = createCustomVideoEncoderFactory(eglBaseContext, true, true, new VideoEncoderSupportedCallback() {
            @Override
            public boolean isSupportedH264(@NonNull MediaCodecInfo info) {
                return true;
            }
        });
        DefaultVideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBaseContext);
        peerConnectionFactory = PeerConnectionFactory
                .builder()
                .setOptions(options)
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory)
                .createPeerConnectionFactory();

        AudioSource createAudioSource = peerConnectionFactory.createAudioSource(WebRTCUtils.buildAudioConstraints());
        audioTrack = peerConnectionFactory.createAudioTrack("local_audio_track", createAudioSource);
        cameraVideoCapturer = WebRTCUtils.createVideoCapture(this);

        if(cameraVideoCapturer != null) {
            VideoSource videoSource = peerConnectionFactory.createVideoSource(cameraVideoCapturer.isScreencast());
            SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create("surface_texture_thread", eglBaseContext);
            cameraVideoCapturer.initialize(surfaceTextureHelper,this,videoSource.getCapturerObserver());
            //使用720P的分辨率
            cameraVideoCapturer.startCapture(localRenderer.getWidth(), localRenderer.getHeight(), 20);
            videoTrack = peerConnectionFactory.createVideoTrack("local_video_track",videoSource);
            // videoTrack.addSink(localRenderer);
        }
        PeerConnection.RTCConfiguration rtcConfig = WebRTCUtils.createRTCConfiguration();
        peerConnection = peerConnectionFactory.createPeerConnection(rtcConfig, new PeerConnectionObserverAdapter(SocketType.CAMERA.getType()) {
            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                super.onIceConnectionChange(iceConnectionState);
                LogUtils.logGGQ("camera onIceConnectionChange");
            }
        });

        if(peerConnection != null) {
            if(videoTrack != null) {
                peerConnection.addTransceiver(videoTrack,new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
            }
            if(audioTrack != null) {
                peerConnection.addTransceiver(audioTrack,new RtpTransceiver.RtpTransceiverInit(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY));
            }
        }

        if(videoTrack != null && !localRenderer.isActivated()) {
            videoTrack.addSink(localRenderer);
        }
    }

    private void createOffer(String user) {
        if(peerConnection != null) {
            peerConnection.createOffer(new SdpObserverAdapter("createOffer") {
                @Override
                public void onCreateSuccess(SessionDescription sdp) {
                    super.onCreateSuccess(sdp);
                    if(sdp != null && sdp.type == SessionDescription.Type.OFFER) {
                        peerConnection.setLocalDescription(new SdpObserverAdapter("setLocalDescription"),sdp);
                        // offer 发送 sdp 给 app
                        if(socketClient != null) {
                            socketClient.sendOffer(SocketType.CAMERA,SocketType.APP,user,sdp);
                        }
                    }
                }
            },new MediaConstraints());
        }
    }

    private void parseAnswer(JSONObject json) {
        if(peerConnection != null) {
            try {
                // 从socket中接收到拉流端的 Answer sdp 后
                String answerSdpDescription = json.getString("sdp");
                SessionDescription answerSdp = new SessionDescription(SessionDescription.Type.ANSWER, answerSdpDescription);
                // 设置远端描述
                peerConnection.setRemoteDescription(new SdpObserverAdapter("setRemoteDescription"),answerSdp);
            } catch (JSONException e) {
                LogUtils.logGGQ("parseAnswer ERROR:" + e.getMessage());
            }
        }
    }

    private void parseIceCandidate(JSONObject json) {
        if(peerConnection != null) {
            try {
                String candidateString = json.getString("candidate");
                String sdpMid = json.getString("sdpMid");
                int sdpMLineIndex = json.getInt("sdpMLineIndex");
                IceCandidate iceCandidate = new IceCandidate(sdpMid, sdpMLineIndex, candidateString);
                peerConnection.addIceCandidate(iceCandidate);
                LogUtils.logGGQ("========addIceCandidate===============>>>>>");
            } catch (JSONException e) {
                LogUtils.logGGQ("parseIceCandidate ERROR:" + e.getMessage());
            }
        }
    }

    private void addItemList(String item) {
        listAdapter.addItem(item);
        lvList.smoothScrollToPosition(listAdapter.getDataList().size() - 1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(cameraVideoCapturer != null) {
            cameraVideoCapturer.dispose();
            try {
                cameraVideoCapturer.stopCapture();
                cameraVideoCapturer = null;
            } catch (InterruptedException e) {
                LogUtils.logGGQ("stopCapture");
            }
        }
        if(socketClient != null) {
            socketClient.close();
        }
        if(localRenderer != null) {
            localRenderer.release();
        }
        if(videoTrack != null) {
            videoTrack.dispose();
        }
        if(audioTrack != null) {
            audioTrack.dispose();
        }
        if(peerConnection != null) {
            peerConnection.dispose();
        }
        if(peerConnectionFactory != null) {
            peerConnectionFactory.dispose();
        }
    }
}